package utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * 反射工具类
 * @author bug-fsh
 */
public class ReflectUtils<T> {

    //get方法前缀
    private final static String PREFIX_GET_METHOD = "get";

    //set方法前缀
    private final static String PREFIX_SET_METHOD = "set";

    //类的方法库
    private final Map<String, List<Method>> methods = new HashMap<String, List<Method>>();

    //反射对象的class对象
    private Class<T> clazz;

    public ReflectUtils(Class<T> clazz) throws Exception {
        if (clazz == null) throw new Exception("反射的class对象不能为空");
        this.clazz = clazz;
        this.initMethods();
    }

    /**
     * 初始化每个反射对象的方法库
     */
    private void initMethods() {
        Method [] methodArr = clazz.getMethods();
        for (Method method : methodArr) {
            String methodName = method.getName();
            List<Method> methodList = null;
            if (!methods.containsKey(methodName)) {
                 methodList = new ArrayList<Method>();
            }else {
                 methodList = methods.get(methodName);
            }
            methodList.add(method);
            methods.put(methodName, methodList);
        }
    }

    /**
     * 通过方法名以及方法参数去方法库中获取Method
     * @param methodName
     * @param params
     * @return
     */
    public Method getMethod(String methodName, Object ... params) throws NoSuchMethodException {
        Method result = null;
        if (methods.containsKey(methodName)) {
            List<Method> methodList = methods.get(methodName);
            Iterator<Method> iterator = methodList.iterator();
            while (iterator.hasNext()) {
                Method temp = iterator.next();
                Parameter [] parameters = temp.getParameters();
                if (ArrayUtils.isEqualOfSize(parameters, params)) {
                    result = temp;
                    break;
                }
            }
        }
        if (result == null) {
            throw new NoSuchMethodException();
        }
        return result;
    }

    /**
     * get对象某个字段值
     * @param object get数据对象
     * @param fieldName 字段名
     * @return
     */
    public <T>Object getValueByFieldName(T object, String fieldName) {
        Object value = null;
        Class<T> clazz = (Class<T>) object.getClass();
        //获取get字段方法名
        String methodName = getMethodNameForGet(fieldName);
        Method method;
        try {
            method = getMethod(methodName);
            //执行get方法，获取值
            value = invokeMethod(object, method);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }

    /**
     * set对象某个字段值
     * @param object set数据对象
     * @param fieldName 字段名
     * @param params set方法参数
     * @param <T>
     * @throws Exception
     */
    public <T>void setValueByFieldName(T object, String fieldName, Object ... params) throws Exception {
        Class<T> clazz = (Class<T>) object.getClass();
        String methodName = getMethodNameForSet(fieldName);
        Method method = getMethod(methodName, params);
        invokeMethod(object, method, params);
    }

    /**
     * 动态执行方法
     * @param object 执行方法对象
     * @param methodName
     * @param params
     * @param <T>
     * @return
     */
    public <T>Object invokeMethod(T object, String methodName, Object ... params) throws Exception {
        Object result = null;
        Method method = getMethod(methodName, params);
        result = invokeMethod(object, method, params);
        return result;
    }

    /**
     * 对象转化map
     * @param object
     * @return
     */
    public Map objectToMap(T object) {
        Map result = new HashMap();
        Field [] fields = getAllFields();
        for (Field field : fields) {
            result.put(field.getName(), getValueByFieldName(object, field.getName()));
        }
        return result;
    }

    /**
     * 动态执行方法
     *@param object 执行方法对象
     * @param method 方法对象
     * @param <T>
     * @return
     * @throws Exception
     */
    private <T>Object invokeMethod(T object, Method method) throws Exception {
        return invokeMethod(object, method, null);
    }

    /**
     * 动态执行方法
     * @param object 执行方法对象
     * @param method 方法对象
     * @param params 执行方法参数
     * @param <T>
     * @return
     * @throws Exception
     */
    private <T>Object invokeMethod(T object, Method method, Object ... params) throws Exception {
        Object result = null;
        //获取方法参数
        Parameter[] parameters = method.getParameters();
        if (!ArrayUtils.isEmpty(parameters)) {
            //验证传入的参数是否与方法参数匹配
            if (ArrayUtils.isEmpty(params)) throw new Exception("参数不能为空");
            if (params.length != params.length) throw new Exception("参数数量有误");
        }
        result = method.invoke(object, params);
        return result;
    }

    /**
     * 获取所有字段包括超类
     * @return
     */
    private Field [] getAllFields() {
        Field [] fields = this.clazz.getDeclaredFields();
        return ArrayUtils.addAll(fields, getSuperFields(clazz.getSuperclass()));
    }

    private Field[] getSuperFields(Class superClass) {
        Field [] fields = null;
        if (superClass == null || !(superClass instanceof  Object)) {
            fields = superClass.getDeclaredFields();
            return ArrayUtils.addAll(fields, getSuperFields(superClass.getSuperclass()));
        }
        return fields;
    }

    /**
     * 通过字段名生成get方法
     * @param fieldName
     * @return
     */
    private String getMethodNameForGet(String fieldName) {
        return PREFIX_GET_METHOD+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
    }

    /**
     * 通过字段名生成get方法
     * @param fieldName
     * @return
     */
    private String getMethodNameForSet(String fieldName) {
        return PREFIX_SET_METHOD+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
    }

}

